Figure 8. Ratio of the maximum value of the peaks measured by the sensors against the maximum value of the peaks measured by the DustTrak before and after calibration for each experiment. The box and whisker plot horizontal lines represent, from bottom to top, the lower quartile, the median and the upper quartile. The vertical lines are drawn to the smallest and the largest data point that fall within 1.5 times the interquartile range below the lower quartile and above the upper quartile respectively. Values outside these range are considered as outliers and plotted as points.


source("utilities.R")
source("variables.R")
require(ggplot2)
require(plotly)
require(cowplot)
require(splitstackshape)
require(ggpubr)
source("Table_2_rh_exploration.R")

df <- prepare_sensor_data(sensor_delay_corrected, cdt)
dt <- prepare_dusttrak_data(dusttrak_file, cdt)

df %>%
  select(sensor, exp, source, date, PM25) %>%
  filter(sensor!="SHT35") %>%
  filter(exp!="") %>%
  group_by(exp, source, sensor) %>%
  nest() -> tmp

tmp %>%
  dplyr::mutate(regression = map(data, ~clean_regression(df =.x,dusttrak = dt, 0))) %>%
  unnest(regression, .drop=TRUE)  ->res



res<-cSplit(indt=res,splitCols = c("sensor"),sep="-",direction="wide",drop=FALSE) %>%
  select(-sensor_2,-sensor_3)->res

res <- res %>%
  flag_site_data(sensor_list)

res_rh_temp <- inner_join(res, summary_rh_temp, by = c("exp"))


#write.csv(select(res_rh_temp,-data), "C:/GitHub/AQ_analysis_lab/lm_no_enclosure_cdt_corr.csv")

res_rh_temp <- res_rh_temp %>%
  mutate(rh_median  = round2(rh_median,0))
df %>% inner_join(select(res_rh_temp, sensor, exp, source, slope, intercept, slope_p_value), by = c("sensor", "exp", "source")) -> df_coeff

df_coeff %>% 
  mutate(PM25_corr = ifelse(slope_p_value<=0.05,(PM25-intercept)/slope), -999) %>%
  filter(PM25_corr != -999) -> df_calibrated

df_calibrated<-cSplit(indt=df_calibrated,splitCols = c("sensor"),sep="-",direction="wide",drop=FALSE) %>%
  select(-sensor_2,-sensor_3)

What does it looks like?

p_opcr1 <- ggplot()+
  geom_line(data = filter(df_calibrated, sensor_1=="OPCR1", exp != ""),
            aes(x = date, y = PM25_corr, group = sensor, colour = sensor, 
                text = paste0("Box: ", site,
                              "<br>Exp: ", exp,
                              "<br>Source: ", source))) +
  labs(y = "PM2.5 (ug/m3)") +
  geom_line(data = filter(dt, exp != ""), aes(x = date,y = pm2.5), colour = "red", linetype = "dashed")+
  scale_x_datetime() + theme_bw() + ggtitle("OPCR1") + theme(plot.title = element_text(hjust = 0.5))+facet_wrap(~exp, ncol = 1, scales = "free_x")
Ignoring unknown aesthetics: text
p_pms5003 <- ggplot()+
  geom_line(data = filter(df_calibrated, sensor_1=="PMS5003", exp != ""),
            aes(x = date, y = PM25_corr, group = sensor, colour = sensor, 
                text = paste0("Box: ", site,
                              "<br>Exp: ", exp,
                              "<br>Source: ", source))) +
  labs(y = "PM2.5 (ug/m3)") +
  geom_line(data = filter(dt, exp != ""), aes(x = date,y = pm2.5), colour = "red", linetype = "dashed")+
  scale_x_datetime() + theme_bw() + ggtitle("PMS5003") + theme(plot.title = element_text(hjust = 0.5))+facet_wrap(~exp, ncol = 1, scales = "free_x")
Ignoring unknown aesthetics: text
p_sds018 <- ggplot()+
  geom_line(data = filter(df_calibrated, sensor_1=="SDS018", exp != ""),
            aes(x = date, y = PM25_corr, group = sensor, colour = sensor, 
                text = paste0("Box: ", site,
                              "<br>Exp: ", exp,
                              "<br>Source: ", source))) +
  labs(y = "PM2.5 (ug/m3)") +
  geom_line(data = filter(dt, exp != ""), aes(x = date,y = pm2.5), colour = "red", linetype = "dashed")+
  scale_x_datetime() + theme_bw() + ggtitle("SDS018") + theme(plot.title = element_text(hjust = 0.5))+facet_wrap(~exp, ncol = 1, scales = "free_x")
Ignoring unknown aesthetics: text
p_sps030 <- ggplot()+
  geom_line(data = filter(df_calibrated, sensor_1=="SPS030", exp != ""),
            aes(x = date, y = PM25_corr, group = sensor, colour = sensor, 
                text = paste0("Box: ", site,
                              "<br>Exp: ", exp,
                              "<br>Source: ", source))) +
  labs(y = "PM2.5 (ug/m3)") +
  geom_line(data = filter(dt, exp != ""), aes(x = date,y = pm2.5), colour = "red", linetype = "dashed")+
  scale_x_datetime() + theme_bw() + ggtitle("SPS030") + theme(plot.title = element_text(hjust = 0.5))+facet_wrap(~exp, ncol = 1, scales = "free_x")
Ignoring unknown aesthetics: text
p_hpma <- ggplot()+
  geom_line(data = filter(df_calibrated, sensor_1=="HPMA115S0", exp != ""),
            aes(x = date, y = PM25_corr, group = sensor, colour = sensor, 
                text = paste0("Box: ", site,
                              "<br>Exp: ", exp,
                              "<br>Source: ", source))) +
  labs(y = "PM2.5 (ug/m3)") +
  geom_line(data = filter(dt, exp != ""), aes(x = date,y = pm2.5), colour = "red", linetype = "dashed")+
  scale_x_datetime() + theme_bw() + ggtitle("HPMA115S0") + theme(plot.title = element_text(hjust = 0.5))+facet_wrap(~exp, ncol = 1, scales = "free_x")
Ignoring unknown aesthetics: text
ggplotly(p_opcr1, dynamicTicks = TRUE)

ggplotly(p_pms5003, dynamicTicks = TRUE)

ggplotly(p_sds018, dynamicTicks = TRUE)

ggplotly(p_sps030, dynamicTicks = TRUE)

ggplotly(p_hpma, dynamicTicks = TRUE)

Now we extract the peaks.

peaks<-readRDS(file="datasets/peaks_characteristics.rds") 


res <- df_calibrated %>%
    filter(date>=as.POSIXct(paste("2019-09-05", peaks[1,]$Start),tz="UTC"), date<=as.POSIXct(paste("2019-09-05", peaks[1,]$End),tz="UTC")) %>%
    group_by(sensor) %>%
  filter(PM25>=1) %>%
  summarise(max_value = max(PM25_corr)) %>%
    mutate(Experiment = peaks[1,]$Experiment, Source = peaks[1,]$Source, Variation= peaks[1,]$Variation, Number = peaks[1,]$Number)
for(row in 2:nrow(peaks)){
  df_calibrated %>%
    filter(date>=as.POSIXct(paste("2019-09-05", peaks[row,]$Start),tz="UTC"), date<=as.POSIXct(paste("2019-09-05", peaks[row,]$End),tz="UTC")) %>%
    group_by(sensor) %>%
      filter(PM25>=1) %>%
  summarise(max_value = max(PM25_corr)) %>%
    mutate(Experiment = peaks[row,]$Experiment, Source = peaks[row,]$Source, Variation= peaks[row,]$Variation, Number = peaks[row,]$Number) -> tmp
  res<-rbind(res,tmp)
  
} 
res %>%
  inner_join(peaks, by=c("Source", "Experiment", "Variation", "Number")) ->res_bind

res_bind<-res_bind %>%
  mutate(ratio = max_value/Concentration)



df2<-cSplit(indt=res_bind,splitCols = c("sensor"),sep="-",direction="wide",drop=FALSE)

df2 %>%
  select(-sensor_2,-sensor_3)->res_bin

Compare with uncalibrated data


res <- df %>%
  filter(date >= as.POSIXct(paste("2019-09-05", peaks[1,]$Start), tz = "UTC"), 
         date <= as.POSIXct(paste("2019-09-05", peaks[1,]$End), tz = "UTC")) %>%
  group_by(sensor) %>%
  filter(PM25 >= 1) %>%
  summarise(max_value = max(PM25)) %>%
  mutate(Experiment = peaks[1, ]$Experiment, Source = peaks[1, ]$Source, Variation = peaks[1, ]$Variation, Number = peaks[1, ]$Number)
for(row in 2:nrow(peaks)){
  df %>%
    filter(date >= as.POSIXct(paste("2019-09-05", peaks[row, ]$Start), tz = "UTC"), 
           date <= as.POSIXct(paste("2019-09-05", peaks[row, ]$End), tz = "UTC")) %>%
    group_by(sensor) %>%
    filter(PM25 >= 1) %>%
  summarise(max_value = max(PM25)) %>%
  mutate(Experiment = peaks[row, ]$Experiment, Source = peaks[row, ]$Source, 
         Variation= peaks[row, ]$Variation, Number = peaks[row, ]$Number) -> tmp
  res<-rbind(res,tmp)
  
} 
res_bind_uncalibrated<- res %>%
  inner_join(peaks, by = c("Source", "Experiment", "Variation", "Number"))  %>%
  mutate(ratio = max_value/Concentration)


res_bind$status <- "Calibrated"
res_bind_uncalibrated$status <- "Not Calibrated"
res_comparison <- rbind(res_bind, res_bind_uncalibrated)

df2<-cSplit(indt=res_comparison,splitCols = c("sensor"),sep="-",direction="wide",drop=FALSE)

df2 %>%
  select(-sensor_2,-sensor_3)->res_comparison

res_comparison$sensor_1 <- factor(res_comparison$sensor_1, levels = c("PMS5003", "SPS030", "HPMA115S0", "SDS018", "OPCR1"))

res_comparison$status <- factor(res_comparison$status, levels = c("Not Calibrated", "Calibrated"))
res_comparison %>%
  filter(Variation == "Peak") %>%
  ggplot(aes(x=Experiment,y=ratio,fill=status)) + 
   geom_boxplot(width=0.8,position=position_dodge(width = 0.8),alpha=0.8,lwd=0.3,fatten=0.8,outlier.size = 0.8,outlier.alpha=0.5,outlier.stroke=0)+
  facet_wrap(sensor_1~Source,ncol=4)+ylab("Ratio sensor/dusttrak")+theme_bw()+
  scale_y_continuous(breaks=pretty(c(-1,6), n=14),sec.axis = dup_axis(name=NULL))+
  theme(axis.text.x = element_text(angle=90,size = 6))->p
p_legend<-get_legend(p)

plot_grid(p_legend)


p<-p+theme(legend.position = "none")
p<-p+  theme(panel.spacing = unit(0, "lines")) +
  theme(plot.margin = unit(c(0,0,0,0), "lines"))
p
ggsave(p,filename = "ratio_comparison_spacing.svg",unit = "mm", width= 180, height = 240)
ggsave(as_ggplot(p_legend),filename = "ratio_comparison_legend.svg",unit = "mm", width= 180, height = 240)

NA
NA
LS0tDQp0aXRsZTogIkZpZ3VyZSA4IC0gQ2FsaWJyYXRpb24gb2YgdGhlIHNlbnNvcnMiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCmVkaXRvcl9vcHRpb25zOiANCiAgY2h1bmtfb3V0cHV0X3R5cGU6IGlubGluZQ0KLS0tDQoNCkZpZ3VyZSA4LiBSYXRpbyBvZiB0aGUgbWF4aW11bSB2YWx1ZSBvZiB0aGUgcGVha3MgbWVhc3VyZWQgYnkgdGhlIHNlbnNvcnMgYWdhaW5zdCB0aGUgbWF4aW11bQ0KdmFsdWUgb2YgdGhlIHBlYWtzIG1lYXN1cmVkIGJ5IHRoZSBEdXN0VHJhayBiZWZvcmUgYW5kIGFmdGVyIGNhbGlicmF0aW9uIGZvciBlYWNoIGV4cGVyaW1lbnQuIFRoZSBib3gNCmFuZCB3aGlza2VyIHBsb3QgaG9yaXpvbnRhbCBsaW5lcyByZXByZXNlbnQsIGZyb20gYm90dG9tIHRvIHRvcCwgdGhlIGxvd2VyIHF1YXJ0aWxlLCB0aGUgbWVkaWFuIGFuZCB0aGUNCnVwcGVyIHF1YXJ0aWxlLiBUaGUgdmVydGljYWwgbGluZXMgYXJlIGRyYXduIHRvIHRoZSBzbWFsbGVzdCBhbmQgdGhlIGxhcmdlc3QgZGF0YSBwb2ludCB0aGF0IGZhbGwgd2l0aGluDQoxLjUgdGltZXMgdGhlIGludGVycXVhcnRpbGUgcmFuZ2UgYmVsb3cgdGhlIGxvd2VyIHF1YXJ0aWxlIGFuZCBhYm92ZSB0aGUgdXBwZXIgcXVhcnRpbGUgcmVzcGVjdGl2ZWx5Lg0KVmFsdWVzIG91dHNpZGUgdGhlc2UgcmFuZ2UgYXJlIGNvbnNpZGVyZWQgYXMgb3V0bGllcnMgYW5kIHBsb3R0ZWQgYXMgcG9pbnRzLg0KDQpgYGB7ciBzZXR1cH0NCg0Kc291cmNlKCJ1dGlsaXRpZXMuUiIpDQpzb3VyY2UoInZhcmlhYmxlcy5SIikNCnJlcXVpcmUoZ2dwbG90MikNCnJlcXVpcmUocGxvdGx5KQ0KcmVxdWlyZShjb3dwbG90KQ0KcmVxdWlyZShzcGxpdHN0YWNrc2hhcGUpDQpyZXF1aXJlKGdncHVicikNCnNvdXJjZSgiVGFibGVfMl9yaF9leHBsb3JhdGlvbi5SIikNCg0KDQpgYGANCg0KDQpgYGB7cn0NCg0KZGYgPC0gcHJlcGFyZV9zZW5zb3JfZGF0YShzZW5zb3JfZGVsYXlfY29ycmVjdGVkLCBjZHQpDQpkdCA8LSBwcmVwYXJlX2R1c3R0cmFrX2RhdGEoZHVzdHRyYWtfZmlsZSwgY2R0KQ0KDQpkZiAlPiUNCiAgc2VsZWN0KHNlbnNvciwgZXhwLCBzb3VyY2UsIGRhdGUsIFBNMjUpICU+JQ0KICBmaWx0ZXIoc2Vuc29yIT0iU0hUMzUiKSAlPiUNCiAgZmlsdGVyKGV4cCE9IiIpICU+JQ0KICBncm91cF9ieShleHAsIHNvdXJjZSwgc2Vuc29yKSAlPiUNCiAgbmVzdCgpIC0+IHRtcA0KDQp0bXAgJT4lDQogIGRwbHlyOjptdXRhdGUocmVncmVzc2lvbiA9IG1hcChkYXRhLCB+Y2xlYW5fcmVncmVzc2lvbihkZiA9LngsZHVzdHRyYWsgPSBkdCwgMCkpKSAlPiUNCiAgdW5uZXN0KHJlZ3Jlc3Npb24sIC5kcm9wPVRSVUUpICAtPnJlcw0KDQoNCg0KcmVzPC1jU3BsaXQoaW5kdD1yZXMsc3BsaXRDb2xzID0gYygic2Vuc29yIiksc2VwPSItIixkaXJlY3Rpb249IndpZGUiLGRyb3A9RkFMU0UpICU+JQ0KICBzZWxlY3QoLXNlbnNvcl8yLC1zZW5zb3JfMyktPnJlcw0KDQpyZXMgPC0gcmVzICU+JQ0KICBmbGFnX3NpdGVfZGF0YShzZW5zb3JfbGlzdCkNCg0KcmVzX3JoX3RlbXAgPC0gaW5uZXJfam9pbihyZXMsIHN1bW1hcnlfcmhfdGVtcCwgYnkgPSBjKCJleHAiKSkNCg0KDQojd3JpdGUuY3N2KHNlbGVjdChyZXNfcmhfdGVtcCwtZGF0YSksICJDOi9HaXRIdWIvQVFfYW5hbHlzaXNfbGFiL2xtX25vX2VuY2xvc3VyZV9jZHRfY29yci5jc3YiKQ0KDQpyZXNfcmhfdGVtcCA8LSByZXNfcmhfdGVtcCAlPiUNCiAgbXV0YXRlKHJoX21lZGlhbiAgPSByb3VuZDIocmhfbWVkaWFuLDApKQ0KYGBgDQoNCg0KYGBge3J9DQpkZiAlPiUgaW5uZXJfam9pbihzZWxlY3QocmVzX3JoX3RlbXAsIHNlbnNvciwgZXhwLCBzb3VyY2UsIHNsb3BlLCBpbnRlcmNlcHQsIHNsb3BlX3BfdmFsdWUpLCBieSA9IGMoInNlbnNvciIsICJleHAiLCAic291cmNlIikpIC0+IGRmX2NvZWZmDQoNCmRmX2NvZWZmICU+JSANCiAgbXV0YXRlKFBNMjVfY29yciA9IGlmZWxzZShzbG9wZV9wX3ZhbHVlPD0wLjA1LChQTTI1LWludGVyY2VwdCkvc2xvcGUpLCAtOTk5KSAlPiUNCiAgZmlsdGVyKFBNMjVfY29yciAhPSAtOTk5KSAtPiBkZl9jYWxpYnJhdGVkDQoNCmRmX2NhbGlicmF0ZWQ8LWNTcGxpdChpbmR0PWRmX2NhbGlicmF0ZWQsc3BsaXRDb2xzID0gYygic2Vuc29yIiksc2VwPSItIixkaXJlY3Rpb249IndpZGUiLGRyb3A9RkFMU0UpICU+JQ0KICBzZWxlY3QoLXNlbnNvcl8yLC1zZW5zb3JfMykNCmBgYA0KDQpXaGF0IGRvZXMgaXQgbG9va3MgbGlrZT8NCg0KYGBge3J9DQpwX29wY3IxIDwtIGdncGxvdCgpKw0KICBnZW9tX2xpbmUoZGF0YSA9IGZpbHRlcihkZl9jYWxpYnJhdGVkLCBzZW5zb3JfMT09Ik9QQ1IxIiwgZXhwICE9ICIiKSwNCiAgICAgICAgICAgIGFlcyh4ID0gZGF0ZSwgeSA9IFBNMjVfY29yciwgZ3JvdXAgPSBzZW5zb3IsIGNvbG91ciA9IHNlbnNvciwgDQogICAgICAgICAgICAgICAgdGV4dCA9IHBhc3RlMCgiQm94OiAiLCBzaXRlLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjxicj5FeHA6ICIsIGV4cCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICI8YnI+U291cmNlOiAiLCBzb3VyY2UpKSkgKw0KICBsYWJzKHkgPSAiUE0yLjUgKHVnL20zKSIpICsNCiAgZ2VvbV9saW5lKGRhdGEgPSBmaWx0ZXIoZHQsIGV4cCAhPSAiIiksIGFlcyh4ID0gZGF0ZSx5ID0gcG0yLjUpLCBjb2xvdXIgPSAicmVkIiwgbGluZXR5cGUgPSAiZGFzaGVkIikrDQogIHNjYWxlX3hfZGF0ZXRpbWUoKSArIHRoZW1lX2J3KCkgKyBnZ3RpdGxlKCJPUENSMSIpICsgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpK2ZhY2V0X3dyYXAofmV4cCwgbmNvbCA9IDEsIHNjYWxlcyA9ICJmcmVlX3giKQ0KDQoNCnBfcG1zNTAwMyA8LSBnZ3Bsb3QoKSsNCiAgZ2VvbV9saW5lKGRhdGEgPSBmaWx0ZXIoZGZfY2FsaWJyYXRlZCwgc2Vuc29yXzE9PSJQTVM1MDAzIiwgZXhwICE9ICIiKSwNCiAgICAgICAgICAgIGFlcyh4ID0gZGF0ZSwgeSA9IFBNMjVfY29yciwgZ3JvdXAgPSBzZW5zb3IsIGNvbG91ciA9IHNlbnNvciwgDQogICAgICAgICAgICAgICAgdGV4dCA9IHBhc3RlMCgiQm94OiAiLCBzaXRlLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjxicj5FeHA6ICIsIGV4cCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICI8YnI+U291cmNlOiAiLCBzb3VyY2UpKSkgKw0KICBsYWJzKHkgPSAiUE0yLjUgKHVnL20zKSIpICsNCiAgZ2VvbV9saW5lKGRhdGEgPSBmaWx0ZXIoZHQsIGV4cCAhPSAiIiksIGFlcyh4ID0gZGF0ZSx5ID0gcG0yLjUpLCBjb2xvdXIgPSAicmVkIiwgbGluZXR5cGUgPSAiZGFzaGVkIikrDQogIHNjYWxlX3hfZGF0ZXRpbWUoKSArIHRoZW1lX2J3KCkgKyBnZ3RpdGxlKCJQTVM1MDAzIikgKyB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkrZmFjZXRfd3JhcCh+ZXhwLCBuY29sID0gMSwgc2NhbGVzID0gImZyZWVfeCIpDQoNCnBfc2RzMDE4IDwtIGdncGxvdCgpKw0KICBnZW9tX2xpbmUoZGF0YSA9IGZpbHRlcihkZl9jYWxpYnJhdGVkLCBzZW5zb3JfMT09IlNEUzAxOCIsIGV4cCAhPSAiIiksDQogICAgICAgICAgICBhZXMoeCA9IGRhdGUsIHkgPSBQTTI1X2NvcnIsIGdyb3VwID0gc2Vuc29yLCBjb2xvdXIgPSBzZW5zb3IsIA0KICAgICAgICAgICAgICAgIHRleHQgPSBwYXN0ZTAoIkJveDogIiwgc2l0ZSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICI8YnI+RXhwOiAiLCBleHAsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiPGJyPlNvdXJjZTogIiwgc291cmNlKSkpICsNCiAgbGFicyh5ID0gIlBNMi41ICh1Zy9tMykiKSArDQogIGdlb21fbGluZShkYXRhID0gZmlsdGVyKGR0LCBleHAgIT0gIiIpLCBhZXMoeCA9IGRhdGUseSA9IHBtMi41KSwgY29sb3VyID0gInJlZCIsIGxpbmV0eXBlID0gImRhc2hlZCIpKw0KICBzY2FsZV94X2RhdGV0aW1lKCkgKyB0aGVtZV9idygpICsgZ2d0aXRsZSgiU0RTMDE4IikgKyB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkrZmFjZXRfd3JhcCh+ZXhwLCBuY29sID0gMSwgc2NhbGVzID0gImZyZWVfeCIpDQoNCnBfc3BzMDMwIDwtIGdncGxvdCgpKw0KICBnZW9tX2xpbmUoZGF0YSA9IGZpbHRlcihkZl9jYWxpYnJhdGVkLCBzZW5zb3JfMT09IlNQUzAzMCIsIGV4cCAhPSAiIiksDQogICAgICAgICAgICBhZXMoeCA9IGRhdGUsIHkgPSBQTTI1X2NvcnIsIGdyb3VwID0gc2Vuc29yLCBjb2xvdXIgPSBzZW5zb3IsIA0KICAgICAgICAgICAgICAgIHRleHQgPSBwYXN0ZTAoIkJveDogIiwgc2l0ZSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICI8YnI+RXhwOiAiLCBleHAsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiPGJyPlNvdXJjZTogIiwgc291cmNlKSkpICsNCiAgbGFicyh5ID0gIlBNMi41ICh1Zy9tMykiKSArDQogIGdlb21fbGluZShkYXRhID0gZmlsdGVyKGR0LCBleHAgIT0gIiIpLCBhZXMoeCA9IGRhdGUseSA9IHBtMi41KSwgY29sb3VyID0gInJlZCIsIGxpbmV0eXBlID0gImRhc2hlZCIpKw0KICBzY2FsZV94X2RhdGV0aW1lKCkgKyB0aGVtZV9idygpICsgZ2d0aXRsZSgiU1BTMDMwIikgKyB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkrZmFjZXRfd3JhcCh+ZXhwLCBuY29sID0gMSwgc2NhbGVzID0gImZyZWVfeCIpDQoNCnBfaHBtYSA8LSBnZ3Bsb3QoKSsNCiAgZ2VvbV9saW5lKGRhdGEgPSBmaWx0ZXIoZGZfY2FsaWJyYXRlZCwgc2Vuc29yXzE9PSJIUE1BMTE1UzAiLCBleHAgIT0gIiIpLA0KICAgICAgICAgICAgYWVzKHggPSBkYXRlLCB5ID0gUE0yNV9jb3JyLCBncm91cCA9IHNlbnNvciwgY29sb3VyID0gc2Vuc29yLCANCiAgICAgICAgICAgICAgICB0ZXh0ID0gcGFzdGUwKCJCb3g6ICIsIHNpdGUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiPGJyPkV4cDogIiwgZXhwLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjxicj5Tb3VyY2U6ICIsIHNvdXJjZSkpKSArDQogIGxhYnMoeSA9ICJQTTIuNSAodWcvbTMpIikgKw0KICBnZW9tX2xpbmUoZGF0YSA9IGZpbHRlcihkdCwgZXhwICE9ICIiKSwgYWVzKHggPSBkYXRlLHkgPSBwbTIuNSksIGNvbG91ciA9ICJyZWQiLCBsaW5ldHlwZSA9ICJkYXNoZWQiKSsNCiAgc2NhbGVfeF9kYXRldGltZSgpICsgdGhlbWVfYncoKSArIGdndGl0bGUoIkhQTUExMTVTMCIpICsgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpK2ZhY2V0X3dyYXAofmV4cCwgbmNvbCA9IDEsIHNjYWxlcyA9ICJmcmVlX3giKQ0KDQpnZ3Bsb3RseShwX29wY3IxLCBkeW5hbWljVGlja3MgPSBUUlVFKQ0KZ2dwbG90bHkocF9wbXM1MDAzLCBkeW5hbWljVGlja3MgPSBUUlVFKQ0KZ2dwbG90bHkocF9zZHMwMTgsIGR5bmFtaWNUaWNrcyA9IFRSVUUpDQpnZ3Bsb3RseShwX3NwczAzMCwgZHluYW1pY1RpY2tzID0gVFJVRSkNCmdncGxvdGx5KHBfaHBtYSwgZHluYW1pY1RpY2tzID0gVFJVRSkNCmBgYA0KDQpOb3cgd2UgZXh0cmFjdCB0aGUgcGVha3MuDQoNCmBgYHtyfQ0KcGVha3M8LXJlYWRSRFMoZmlsZT0iZGF0YXNldHMvcGVha3NfY2hhcmFjdGVyaXN0aWNzLnJkcyIpIA0KDQoNCnJlcyA8LSBkZl9jYWxpYnJhdGVkICU+JQ0KICAgIGZpbHRlcihkYXRlPj1hcy5QT1NJWGN0KHBhc3RlKCIyMDE5LTA5LTA1IiwgcGVha3NbMSxdJFN0YXJ0KSx0ej0iVVRDIiksIGRhdGU8PWFzLlBPU0lYY3QocGFzdGUoIjIwMTktMDktMDUiLCBwZWFrc1sxLF0kRW5kKSx0ej0iVVRDIikpICU+JQ0KICAgIGdyb3VwX2J5KHNlbnNvcikgJT4lDQogIGZpbHRlcihQTTI1Pj0xKSAlPiUNCiAgc3VtbWFyaXNlKG1heF92YWx1ZSA9IG1heChQTTI1X2NvcnIpKSAlPiUNCiAgICBtdXRhdGUoRXhwZXJpbWVudCA9IHBlYWtzWzEsXSRFeHBlcmltZW50LCBTb3VyY2UgPSBwZWFrc1sxLF0kU291cmNlLCBWYXJpYXRpb249IHBlYWtzWzEsXSRWYXJpYXRpb24sIE51bWJlciA9IHBlYWtzWzEsXSROdW1iZXIpDQpmb3Iocm93IGluIDI6bnJvdyhwZWFrcykpew0KICBkZl9jYWxpYnJhdGVkICU+JQ0KICAgIGZpbHRlcihkYXRlPj1hcy5QT1NJWGN0KHBhc3RlKCIyMDE5LTA5LTA1IiwgcGVha3Nbcm93LF0kU3RhcnQpLHR6PSJVVEMiKSwgZGF0ZTw9YXMuUE9TSVhjdChwYXN0ZSgiMjAxOS0wOS0wNSIsIHBlYWtzW3JvdyxdJEVuZCksdHo9IlVUQyIpKSAlPiUNCiAgICBncm91cF9ieShzZW5zb3IpICU+JQ0KICAgICAgZmlsdGVyKFBNMjU+PTEpICU+JQ0KICBzdW1tYXJpc2UobWF4X3ZhbHVlID0gbWF4KFBNMjVfY29ycikpICU+JQ0KICAgIG11dGF0ZShFeHBlcmltZW50ID0gcGVha3Nbcm93LF0kRXhwZXJpbWVudCwgU291cmNlID0gcGVha3Nbcm93LF0kU291cmNlLCBWYXJpYXRpb249IHBlYWtzW3JvdyxdJFZhcmlhdGlvbiwgTnVtYmVyID0gcGVha3Nbcm93LF0kTnVtYmVyKSAtPiB0bXANCiAgcmVzPC1yYmluZChyZXMsdG1wKQ0KICANCn0gDQpyZXMgJT4lDQogIGlubmVyX2pvaW4ocGVha3MsIGJ5PWMoIlNvdXJjZSIsICJFeHBlcmltZW50IiwgIlZhcmlhdGlvbiIsICJOdW1iZXIiKSkgLT5yZXNfYmluZA0KDQpyZXNfYmluZDwtcmVzX2JpbmQgJT4lDQogIG11dGF0ZShyYXRpbyA9IG1heF92YWx1ZS9Db25jZW50cmF0aW9uKQ0KDQoNCg0KcmVzX2JpbjwtY1NwbGl0KGluZHQ9cmVzX2JpbmQsc3BsaXRDb2xzID0gYygic2Vuc29yIiksc2VwPSItIixkaXJlY3Rpb249IndpZGUiLGRyb3A9RkFMU0UpICU+JQ0KICBzZWxlY3QoLXNlbnNvcl8yLC1zZW5zb3JfMykNCg0KDQpgYGANCg0KDQojIENvbXBhcmUgd2l0aCB1bmNhbGlicmF0ZWQgZGF0YQ0KDQpgYGB7cn0NCg0KcmVzIDwtIGRmICU+JQ0KICBmaWx0ZXIoZGF0ZSA+PSBhcy5QT1NJWGN0KHBhc3RlKCIyMDE5LTA5LTA1IiwgcGVha3NbMSxdJFN0YXJ0KSwgdHogPSAiVVRDIiksIA0KICAgICAgICAgZGF0ZSA8PSBhcy5QT1NJWGN0KHBhc3RlKCIyMDE5LTA5LTA1IiwgcGVha3NbMSxdJEVuZCksIHR6ID0gIlVUQyIpKSAlPiUNCiAgZ3JvdXBfYnkoc2Vuc29yKSAlPiUNCiAgZmlsdGVyKFBNMjUgPj0gMSkgJT4lDQogIHN1bW1hcmlzZShtYXhfdmFsdWUgPSBtYXgoUE0yNSkpICU+JQ0KICBtdXRhdGUoRXhwZXJpbWVudCA9IHBlYWtzWzEsIF0kRXhwZXJpbWVudCwgU291cmNlID0gcGVha3NbMSwgXSRTb3VyY2UsIFZhcmlhdGlvbiA9IHBlYWtzWzEsIF0kVmFyaWF0aW9uLCBOdW1iZXIgPSBwZWFrc1sxLCBdJE51bWJlcikNCmZvcihyb3cgaW4gMjpucm93KHBlYWtzKSl7DQogIGRmICU+JQ0KICAgIGZpbHRlcihkYXRlID49IGFzLlBPU0lYY3QocGFzdGUoIjIwMTktMDktMDUiLCBwZWFrc1tyb3csIF0kU3RhcnQpLCB0eiA9ICJVVEMiKSwgDQogICAgICAgICAgIGRhdGUgPD0gYXMuUE9TSVhjdChwYXN0ZSgiMjAxOS0wOS0wNSIsIHBlYWtzW3JvdywgXSRFbmQpLCB0eiA9ICJVVEMiKSkgJT4lDQogICAgZ3JvdXBfYnkoc2Vuc29yKSAlPiUNCiAgICBmaWx0ZXIoUE0yNSA+PSAxKSAlPiUNCiAgc3VtbWFyaXNlKG1heF92YWx1ZSA9IG1heChQTTI1KSkgJT4lDQogIG11dGF0ZShFeHBlcmltZW50ID0gcGVha3Nbcm93LCBdJEV4cGVyaW1lbnQsIFNvdXJjZSA9IHBlYWtzW3JvdywgXSRTb3VyY2UsIA0KICAgICAgICAgVmFyaWF0aW9uPSBwZWFrc1tyb3csIF0kVmFyaWF0aW9uLCBOdW1iZXIgPSBwZWFrc1tyb3csIF0kTnVtYmVyKSAtPiB0bXANCiAgcmVzPC1yYmluZChyZXMsdG1wKQ0KICANCn0gDQpyZXNfYmluZF91bmNhbGlicmF0ZWQ8LSByZXMgJT4lDQogIGlubmVyX2pvaW4ocGVha3MsIGJ5ID0gYygiU291cmNlIiwgIkV4cGVyaW1lbnQiLCAiVmFyaWF0aW9uIiwgIk51bWJlciIpKSAgJT4lDQogIG11dGF0ZShyYXRpbyA9IG1heF92YWx1ZS9Db25jZW50cmF0aW9uKQ0KDQoNCnJlc19iaW5kJHN0YXR1cyA8LSAiQ2FsaWJyYXRlZCINCnJlc19iaW5kX3VuY2FsaWJyYXRlZCRzdGF0dXMgPC0gIk5vdCBDYWxpYnJhdGVkIg0KcmVzX2NvbXBhcmlzb24gPC0gcmJpbmQocmVzX2JpbmQsIHJlc19iaW5kX3VuY2FsaWJyYXRlZCkNCg0KZGYyPC1jU3BsaXQoaW5kdD1yZXNfY29tcGFyaXNvbixzcGxpdENvbHMgPSBjKCJzZW5zb3IiKSxzZXA9Ii0iLGRpcmVjdGlvbj0id2lkZSIsZHJvcD1GQUxTRSkNCg0KZGYyICU+JQ0KICBzZWxlY3QoLXNlbnNvcl8yLC1zZW5zb3JfMyktPnJlc19jb21wYXJpc29uDQoNCg0KDQpgYGANCg0KYGBge3J9DQoNCnJlc19jb21wYXJpc29uJHNlbnNvcl8xIDwtIGZhY3RvcihyZXNfY29tcGFyaXNvbiRzZW5zb3JfMSwgbGV2ZWxzID0gYygiUE1TNTAwMyIsICJTUFMwMzAiLCAiSFBNQTExNVMwIiwgIlNEUzAxOCIsICJPUENSMSIpKQ0KDQpyZXNfY29tcGFyaXNvbiRzdGF0dXMgPC0gZmFjdG9yKHJlc19jb21wYXJpc29uJHN0YXR1cywgbGV2ZWxzID0gYygiTm90IENhbGlicmF0ZWQiLCAiQ2FsaWJyYXRlZCIpKQ0KcmVzX2NvbXBhcmlzb24gJT4lDQogIGZpbHRlcihWYXJpYXRpb24gPT0gIlBlYWsiKSAlPiUNCiAgZ2dwbG90KGFlcyh4PUV4cGVyaW1lbnQseT1yYXRpbyxmaWxsPXN0YXR1cykpICsgDQogICBnZW9tX2JveHBsb3Qod2lkdGg9MC44LHBvc2l0aW9uPXBvc2l0aW9uX2RvZGdlKHdpZHRoID0gMC44KSxhbHBoYT0wLjgsbHdkPTAuMyxmYXR0ZW49MC44LG91dGxpZXIuc2l6ZSA9IDAuOCxvdXRsaWVyLmFscGhhPTAuNSxvdXRsaWVyLnN0cm9rZT0wKSsNCiAgZmFjZXRfd3JhcChzZW5zb3JfMX5Tb3VyY2UsbmNvbD00KSt5bGFiKCJSYXRpbyBzZW5zb3IvZHVzdHRyYWsiKSt0aGVtZV9idygpKw0KICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzPXByZXR0eShjKC0xLDYpLCBuPTE0KSxzZWMuYXhpcyA9IGR1cF9heGlzKG5hbWU9TlVMTCkpKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZT05MCxzaXplID0gNikpLT5wDQpwX2xlZ2VuZDwtZ2V0X2xlZ2VuZChwKQ0KDQpwbG90X2dyaWQocF9sZWdlbmQpDQoNCnA8LXArdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQ0KcDwtcCsgIHRoZW1lKHBhbmVsLnNwYWNpbmcgPSB1bml0KDAsICJsaW5lcyIpKSArDQogIHRoZW1lKHBsb3QubWFyZ2luID0gdW5pdChjKDAsMCwwLDApLCAibGluZXMiKSkNCnANCiNnZ3NhdmUocCxmaWxlbmFtZSA9ICJyYXRpb19jb21wYXJpc29uX3NwYWNpbmcuc3ZnIix1bml0ID0gIm1tIiwgd2lkdGg9IDE4MCwgaGVpZ2h0ID0gMjQwKQ0KIyhhc19nZ3Bsb3QocF9sZWdlbmQpLGZpbGVuYW1lID0gInJhdGlvX2NvbXBhcmlzb25fbGVnZW5kLnN2ZyIsdW5pdCA9ICJtbSIsIHdpZHRoPSAxODAsIGhlaWdodCA9IDI0MCkNCg0KDQpgYGANCg0KDQoNCg0K